const std = @import("std");
const fs = std.fs;
const Compiler = @import("Compiler.zig");

pub fn main() anyerror!void {
    var gpa: std.heap.GeneralPurposeAllocator(.{}) = .{};
    defer std.debug.assert(gpa.deinit() == .ok);
    const allocator = gpa.allocator();

    const args = try std.process.argsAlloc(allocator);
    defer std.process.argsFree(allocator, args);

    if (args.len < 2) {
        std.log.err("Usage: {s} <dir/file.jack>", .{args[0]});
        return;
    }

    const filename = args[1];
    const cwd = fs.cwd();

    const file = try cwd.openFile(filename, .{});
    defer file.close();
    const stat = try file.stat();

    if (stat.kind == .directory) {
        const output_dirname = filename;
        var dir = try cwd.openDir(output_dirname, .{ .iterate = true });
        defer dir.close();

        // iterate over the directory to find all the .jack files in it
        var iter = dir.iterate();
        while (try iter.next()) |entry| {
            if (entry.kind == .file and std.mem.endsWith(u8, entry.name, ".jack")) {
                try compile(allocator, dir, entry.name);
            }
        }
    } else if (stat.kind == .file) {
        if (std.mem.endsWith(u8, filename, ".jack")) {
            const output_dirname = fs.path.dirname(filename) orelse "./";
            var dir = try cwd.openDir(output_dirname, .{});
            defer dir.close();

            try compile(allocator, dir, fs.path.basename(filename));
        }
    } else {
        unreachable;
    }
}

fn compile(gpa: std.mem.Allocator, dir: fs.Dir, input: []const u8) !void {
    const source = try dir.readFileAlloc(gpa, input, std.math.maxInt(usize));
    defer gpa.free(source);

    const output = try std.mem.concat(gpa, u8, &.{ fs.path.stem(input), ".xml" });
    defer gpa.free(output);

    const xmlfile = try dir.createFile(output, .{ .truncate = true });
    defer xmlfile.close();

    var compiler: Compiler = try .init(gpa, source, xmlfile.writer());
    defer compiler.deinit();
    compiler.compile("class");
}
